/*
Copyright (c) 2012 the VATSIM Connector project

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef __LIBVATCONN__MESSAGES_H__
#define __LIBVATCONN__MESSAGES_H__

#include "libvatconn.h"
#include "stringify.h"

#define VCON_MSG_TYPE_NUM(A, B, C) (((A) << 16) | ((B) << 8) | (C))

#define VCON_MSG_TYPE(STR, A, B, C) \
  static char const* typeStr() { return (STR); } \
  enum { typeNum = VCON_MSG_TYPE_NUM(A, B, C) };

/**
 * Message providing position and status of other traffic.
 *
 * Sent by the host connector to the visualization plugin.
 */
struct VconMsgPlanePosition
{
  VCON_MSG_TYPE("#PP", '#', 'P', 'P')

  std::string callsign;	///< callsign
  std::string airline;	///< airline (3 letter ICAO code)
  std::string aircraft;	///< aircraft type (4 letter ICAO code)
  double lat;	///< latitude in degrees (negative is south)
  double lon;	///< longitude in degrees (negative is west)
  int alt;	///< altitude in feet amsl
  int hdg;	///< heading in degrees TODO true or mag?
  bool gears;	///< true if gear extended
  bool flaps;	///< true if flaps extended
  bool lights;	///< true if landing or taxi lights on
};

/**
 * Message providing weather conditions.
 *
 * Sent by the host connector to the visualization plugin.
 */
struct VconMsgWeather
{
  VCON_MSG_TYPE("#WX", '#', 'W', 'X')

  VconWxMsgSubtype subtype; ///< message subtype (METAR, CLOUDS, WINDS, or TEMPS)
  std::string metar;	    ///< METAR string for the METAR subtype
  union
  {
    struct
    {
      int layer;	///< which cloud layer; 1, 2, or 3
      int base;		///< cloud base in feet amsl
      int top;		///< cloud tops in feet amsl
      int coverage;	///< sky coverage in octas; 0 is clear, 8 is overcast
      bool icing;	///< true if icing possible
      int turbulence;	///< turbulence (0-255)
      int tsdev;	///< deviation of thunderstorm layer; 0 if not a TS
    } clouds;		///< parameters for the clouds subtype
    struct
    {
      int layer;	///< which wind layer; 1, 2, 3, or 4
      int base;		///< layer base in feet amsl
      int top;		///< layer tops in feet amsl
      int direction;	///< direction wind is from in degrees TODO true or mag?
      int speed;	///< wind speed in knots
      bool gusting;	///< true if gusting
      int turbulence;	///< turbulence (0-255)
    } winds;		///< parameters for the winds subtype
    struct
    {
      int layer;	///< which temperature layer; 1, 2, 3, or 4
      int ceiling;	///< temperature layer ceiling in feet amsl
      int temperature;	///< temperature in degrees C
      int pressure;	///< pressure in points of Hg (100 points = 1 inch)
    } temps;		///< parameters for the temperatures subtype
  };
};

/**
 * Message including some arbitrary text.
 *
 * Sent by the host connector to the visualization plugin, or by the
 * visualization plugin to the host connector.
 */
struct VconMsgText
{
  VCON_MSG_TYPE("#MG", '#', 'M', 'G')

  std::string message;	///< text message
};

/**
 * Message forcing the sim to set the specified squawk code.
 *
 * Sent by the host connector to the visualization plugin.
 */
struct VconMsgSquawk
{
  VCON_MSG_TYPE("#SQ", '#', 'S', 'Q')

  VconSquawk squawk; ///< squawk code
};

/**
 * Message providing position and status of our airplane.
 *
 * Sent by the visualization plugin to the host connector.
 */
struct VconMsgOwnPlane
{
  VCON_MSG_TYPE("#OP", '#', 'O', 'P')

  double lat;	///< latitude in degrees (negative is south)
  double lon;	///< longitude in degrees (negative is west)
  int alt;	///< altitude in feet amsl
  int hdg;	///< heading in degrees TODO true or mag?
  bool gears;	///< true if gear extended
  bool flaps;	///< true if flaps extended
  bool lights;	///< true if landing or taxi lights on
  std::string nearestAirport; ///< ICAO code of the plane's nearest airport
};

/**
 * Message providing the frequency setting on one of our COM radios.
 *
 * Sent by the visualization plugin to the host connector.
 */
struct VconMsgRadioFreq
{
  VCON_MSG_TYPE("#RX", '#', 'R', 'X')

  int radio;	      ///< which radio? 1 for COM1, 2 for COM2, etc.
  VconFreq frequency;  ///< frequency
};

/**
 * Message providing the user selected squawk code and mode.
 *
 * Sent by the visualization plugin to the host connector.
 */
struct VconMsgChangeSquawk
{
  VCON_MSG_TYPE("#CQ", '#', 'C', 'Q')

  VconSquawk squawk; ///< squawk code
  bool ident;	    ///< true if currently squawking ident
  bool mode;	    ///< true if squawking mode C
};

inline bool VconDestringifyMessage(VconMsgPlanePosition& msg, char const* str, size_t len)
{
  VconDestringifier destr (str, len);
  destr	>> msg.callsign >> msg.airline >> msg.aircraft >> msg.lat >> msg.lon
	>> msg.alt >> msg.hdg >> msg.gears >> msg.flaps >> msg.lights;
  return ! destr.error();
}

inline bool VconDestringifyMessage(VconMsgWeather& msg, char const* str, size_t len)
{
  VconDestringifier destr (str, len);
  destr	>> msg.subtype;
  if (destr.error()) return false;
  switch (msg.subtype)
  {
  case vconWxMETAR:
    destr >> msg.metar;
    break;
  case vconWxCLOUDS:
    destr >> msg.clouds.layer >> msg.clouds.base >> msg.clouds.top
	  >> msg.clouds.coverage >> msg.clouds.icing >> msg.clouds.turbulence
	  >> msg.clouds.tsdev;
    break;
  case vconWxWINDS:
    destr >> msg.winds.layer >> msg.winds.base >> msg.winds.top
	  >> msg.winds.direction >> msg.winds.speed >> msg.winds.gusting
	  >> msg.winds.turbulence;
    break;
  case vconWxTEMPS:
    destr >> msg.temps.layer >> msg.temps.ceiling >> msg.temps.temperature
	  >> msg.temps.pressure;
    break;
  default: return false;
  }
  return ! destr.error();
}

inline bool VconDestringifyMessage(VconMsgText& msg, char const* str, size_t len)
{
  VconDestringifier destr (str, len, false);
  destr	>> msg.message;
  return ! destr.error();
}

inline std::string VconStringifyMessage(VconMsgText const& msg)
{
  VconStringifier str;
  str << msg.message;
  return str.stringify();
}

inline bool VconDestringifyMessage(VconMsgSquawk& msg, char const* str, size_t len)
{
  VconDestringifier destr (str, len);
  destr	>> msg.squawk;
  return ! destr.error();
}

inline std::string VconStringifyMessage(VconMsgOwnPlane const& msg)
{
  VconStringifier str;
  str << msg.lat << msg.lon << msg.alt << msg.hdg << msg.gears << msg.flaps
      << msg.lights << msg.nearestAirport;
  return str.stringify();
}

inline std::string VconStringifyMessage(VconMsgRadioFreq const& msg)
{
  VconStringifier str;
  str << msg.radio << msg.frequency;
  return str.stringify();
}

inline std::string VconStringifyMessage(VconMsgChangeSquawk const& msg)
{
  VconStringifier str;
  str << msg.squawk << msg.ident << msg.mode;
  return str.stringify();
}

#endif //__LIBVATCONN__MESSAGES_H__